﻿using System.ComponentModel.DataAnnotations;

namespace _3_NovaAdmin.Domain.Abstractions;

/// <summary>
/// 实体基类（所有实体继承此类）
/// </summary>
public abstract class Entity : IEquatable<Entity>
{
    /// <summary>
    /// 主键比较专用抽象方法
    /// </summary>
    public abstract object[] GetKeys();

    /// <summary>
    /// 判断是否是瞬态对象（未持久化）
    /// </summary>
    public virtual bool IsTransient()
    {
        return GetKeys().All(k =>
            k == null ||
            k.Equals(GetDefaultValue(k.GetType())));
    }

    #region Equality 实现
    public override bool Equals(object obj)
    {
        return Equals(obj as Entity);
    }

    public bool Equals(Entity other)
    {
        if (other is null) return false;
        if (ReferenceEquals(this, other)) return true;
        if (GetType() != other.GetType()) return false;
        if (IsTransient() || other.IsTransient()) return false;
        return GetKeys().SequenceEqual(other.GetKeys());
    }

    public override int GetHashCode()
    {
        return IsTransient()
            ? base.GetHashCode()
            : GetKeys()
                .Select(k => k?.GetHashCode() ?? 0)
                .Aggregate((x, y) => x ^ y);
    }

    public static bool operator ==(Entity left, Entity right) =>
        Equals(left, right);

    public static bool operator !=(Entity left, Entity right) =>
        !Equals(left, right);
    #endregion

    #region Helper Methods
    private static object GetDefaultValue(Type type) =>
        type.IsValueType ? Activator.CreateInstance(type) : null;

    public override string ToString() =>
        $"[{GetType().Name}] Keys = {string.Join(",", GetKeys())}";
    #endregion

    #region 领域事件
    private List<IDomainEvent> _domainEvents;
    [SugarColumn(IsIgnore = true)]
    public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();

    public void AddDomainEvent(IDomainEvent eventItem)
    {
        _domainEvents = _domainEvents ?? new List<IDomainEvent>();
        _domainEvents.Add(eventItem);
    }

    public void RemoveDomainEvent(IDomainEvent eventItem)
    {
        _domainEvents?.Remove(eventItem);
    }

    public void ClearDomainEvents()
    {
        _domainEvents?.Clear();
    }
    #endregion
}

/// <summary>
/// 单主键实体基类（自动处理主键比较）
/// </summary>
/// <typeparam name="TKey">主键类型</typeparam>
public abstract class Entity<TKey> : AuditableEntity<TKey>, IEntity<TKey>
    where TKey : notnull
{
    /// <summary>
    /// EF Core 会默认将Id这个属性作为主键
    /// </summary>
    public virtual TKey Id { get; protected set; }

    /// <summary>  
    /// 自动将单主键转换为复合主键数组  
    /// </summary>   
    public override object[] GetKeys() => new object[] { Id };
}

/// <summary>
/// 复合主键实体基类（必须显式实现主键比较）
/// </summary>
/// <typeparam name="TKey"></typeparam>
public abstract class CompositeEntity<TKey> : AuditableEntity<TKey>, ICompositeEntity<TKey>
    where TKey : notnull
{
    /// <summary>
    /// 必须由子类实现复合主键逻辑
    /// </summary>
    public abstract override object[] GetKeys();
}